/*
 * JarFile.c
 *
 *  Created on: 2011-7-9
 *      Author: gexiao
 */
/*
 * Access the contents of a Jar file.
 *
 * This isn't actually concerned with any of the Jar-like elements; it
 * just wants a zip archive with "classes.dex" inside. In Android the most
 * common example is ".apk".
 */
#include "Dalvik.h"

#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
static const char* kDexInJarName = "classes.dex";

/*
 * Attempt to open a file whose name is similar to <fileName>,
 * but with the supplied suffix. E.g.,
 * openAlternateSuffix("Home.apk","dex","O_RDONLY") will attempt
 * to open "Home.dex". If the open succeeds, a pointer to a
 * malloc()ed copy of the opened file name will be put in <*pCachedName>
 *
 * <flags> is passed directly to open(). O_CREAT is not supported.
 */
static int openAlternateSuffix(const char* fileName, const char* suffix,
		int flags, char** pCachedName) {
	char *buf, *c;
	size_t fileNameLen = strlen(fileName);
	size_t suffixLen = strlen(suffix);
	size_t bufLen = fileNameLen + suffixLen + 1;
	int fd = -1;

	buf = malloc(bufLen);
	if (buf == NULL) {
		errno = ENOMEM;
		return -1;
	}

	/*
	 * Copy the original filename into the buffer, find
	 * the last dot, and copy the suffix to just after it.
	 */memcpy(buf,fileName,fileNameLen+1);
	c = strrchr(buf, '.');
	if (c == NULL) {
		errno = ENOENT;
		goto bail;
	}
	memcpy(c+1,suffix,suffixLen+1);
	fd = open(buf, flags);
	if (fd >= 0) {
		*pCachedName = buf;
		return fd;
	}
	bail: free(buf);
	return -1;
}
/*
 * Open a Jar file. It's okey if it's just a Zip archive without all of
 * the Jar trimmings,but we do insist on finding "classes.dex" inside
 * or an appropriately-named ".odex" file alongside.
 *
 * If "isBootstrap" is not set, the optimizer/verifier regards this DEX as
 * being part of a different class loader.
 */
int dvmJarFileOpen(const char* fileName, JarFile** ppJarFile, bool isBootstrap) {
	ZipArchive archive;
	DvmDex* pDvmDex = NULL;
	char* cachedName = NULL;
	bool archiveOpen = false;
	bool locked = false;
	int fd = -1;
	int result = -1;

	/*
	 * Even if we're not going to look at the archive, we need to
	 * open it so we can stuff it into ppJarFile.
	 */
	if (dexZipOpenArchive(fileName, &archive) != 0)
		goto bail;
	archiveOpen = true;

	/*
	 * If we fork/exec into dexopt, don't let it inherit the archive's fd.
	 */
	dvmSetCloseOnExec(dexZipGetArchiveFd(&archive));

	/*
	 * First, look for a ".odex" alongside the jar file. It will
	 * have the same name/path except for the extension.
	 */
	fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName);
	if (fd >= 0) {
		if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) {
			free(cachedName);
			close(fd);
			fd = -1;
			goto tryArchive;
		} else {
			//LOGV("%s odex has good dependencies\n", fileName);
			//TODO: make sure that the .odex actually corresponds
			//      to the classes.dex inside the archive (if present).
			//      For typical use there will be no classes.dex.
		}
	} else {
		ZipEntry entry;
tryArchive:
		/*
		 * Missing or out-of-date .odex. Look inside the jar.
		 */
		entry = dexZipFindEntry(&archive, kDexInJarName);

		if (entry != NULL) {
			bool newFile = false;

			/*
			 * We've found the one we want. See if there's an up-to-date copy
			 * in the cache.
			 *
			 * If a stale .odex file is present and classes.dex exists in
			 * the archive, this will *not* return an fd pointing to the
			 * .odex file;the fd will point into dalvik-cache like any other
			 * jar.
			 */
			fd = dvmOpenCachedDexFile(fileName, kDexInJarName,
					dexGetZipEntryModTime(&archive, entry),
					dexGetZipEntryCrc32(&archive, entry), isBootstrap,
					&cachedName, &newFile, true);
			if (fd < 0) {
				goto bail;
			}
			locked = true;

			/*
			 * If fd points to a new file (because there was no cached version,
			 * or the cached version was stale), generate the optimized DEX.
			 * The file descriptor returnde is still locked, and is positioned
			 * just past the optimization header.
			 */
			if (newFile) {
				u8 startWhen, extractWhen, endWhen;
				bool result;
				off_t dexOffset, fileLen;
				//some code here
			}
		} else {
			goto bail;
		}
	}

	/*
	 * Map the cached version. This immediately rewinds the fd, so it
	 * doesn't have to be seeked anywhere in particular.
	 */
	if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0) {
		goto bail;
	}

	if (locked) {
		//unlock the fd
		if (!dvmUnlockCachedDexFile(fd)) {
			goto bail;
		}
		locked = false;
	}

	*ppJarFile = (JarFile*) calloc(1, sizeof(JarFile));
	(*ppJarFile)->archive = archive;
	(*ppJarFile)->cacheFileName = cachedName;
	(*ppJarFile)->pDvmDex = pDvmDex;
	cachedName = NULL;
	result = 0;
	bail:
	//some code here
	return result;
}

